Skip to content

fix(concerto-cto): safely handle null default values and validator bounds in Printer - #1601

Open
Siddharth-Dangi wants to merge 1 commit into
accordproject:mainfrom
Siddharth-Dangi:fix/printer-null-bounds-defaults
Open

Siddharth-Dangi wants to merge 1 commit into
accordproject:mainfrom
Siddharth-Dangi:fix/printer-null-bounds-defaults

Conversation

@Siddharth-Dangi

@Siddharth-Dangi Siddharth-Dangi commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

In @accordproject/concerto-cto, Printer.toCTO converts a Concerto Metamodel AST into CTO text format. When processing an AST where optional fields have explicit null values or omitted bounds (such as ASTs deserialized from JSON models or generated programmatically):

  1. TypeError crash on defaults: For IntegerProperty and LongProperty, Printer.toCTO called mm.defaultValue.toString() because it checked !== undefined. If defaultValue: null, it crashed with TypeError: Cannot read properties of null (reading 'toString').
  2. Invalid CTO syntax on length bounds: For StringLengthValidator, if both bounds were null or omitted, Printer.toCTO emitted length=[,], which fails parsing with At least one length bound must be specified.
  3. Invalid model validation on domain/size bounds: For DoubleDomainValidator, IntegerDomainValidator, LongDomainValidator, and CollectionSizeValidator, null or omitted bounds emitted range=[null, ...] or range=[,]/size=[,], which fail model validation with IllegalModelException.
  4. Double bound corruption: For DoubleDomainValidator, toDoubleString(null) evaluated Number(null) as 0, turning unbounded lower bounds into 0.0.
  5. Quote escaping in ObjectProperty: ObjectProperty default values containing quotes were interpolated directly without escaping (default="${mm.defaultValue}"), breaking CTO parsing.
  6. Spurious formatting with empty decorators: Metamodel elements with decorators: [] emitted spurious newlines and whitespace indentation.
  7. Regex flags and pattern handling: Passing flags: null threw in RegExp, bypassing pattern normalization and slash escaping, while pattern: null fabricated regex=/null/.

Solution

  • Use NullUtil.isNull from @accordproject/concerto-util to safely guard against null and undefined while preserving valid falsy values (0, 0.0, "", false).
  • Guard range, length, and size validators to only emit clauses when at least one bound exists.
  • Escape ObjectProperty default values with toStringLiteral, ensuring quotes and backslashes round-trip cleanly.
  • Defensively exit early in decoratorsFromMetaModel when the decorators array is empty.
  • Sanitize regex flags (flags || undefined) and guard pattern so regex normalization and forward-slash escaping succeed cleanly.
  • Add comprehensive regression tests in packages/concerto-cto/test/printer.js achieving 100% statement, branch, function, and line coverage.

Checklist

  • Code conforms to repository style conventions
  • Tests added and passing (192/192 tests, 100% statement/branch/function/line coverage in concerto-cto)
  • Linter, license check (licchk), and JSDoc pass cleanly
  • Commit message conforms to Conventional Commits and includes DCO sign-off (Signed-off-by)

@Siddharth-Dangi

Copy link
Copy Markdown
Author

Hi team (@jamieshorten @mttrbrts) — friendly check-in on this PR. All core builds, unit tests, DCO, and CodeQL security checks are passing green. (The external conformance test failure is the known upstream expectation mismatch on map value types present across main).

Happy to address any feedback or adjust anything whenever you have a moment to review. Thank you!

@mira-accord-project

Copy link
Copy Markdown

Thanks for the PR, @Siddharth-Dangi, and for the patience while it waited. Handling null default values and validator bounds in the CTO Printer is a useful hardening. Maintainers have been requested to review. The conformance check has since been fixed upstream, so a rerun of the checks should now go through. Please push an empty commit or rebase to trigger it if it doesn't start on its own.

Drafted by an Accord Project agent, reviewed by a maintainer.

@Siddharth-Dangi
Siddharth-Dangi force-pushed the fix/printer-null-bounds-defaults branch from 8f7c401 to 62fe262 Compare November 4, 2026 09:48
@Siddharth-Dangi

Copy link
Copy Markdown
Author

Thanks @mira-accord-project! I've cleanly rebased the branch onto the latest upstream main containing the conformance fix. Looking forward to the maintainer review!

@Siddharth-Dangi
Siddharth-Dangi force-pushed the fix/printer-null-bounds-defaults branch from 62fe262 to f0a7dfe Compare November 11, 2026 04:58
…unds in Printer

Metamodel AST instances can represent optional or unbounded properties
using explicit null values (e.g. deserialized from JSON metamodels).
Previously, Printer.toCTO only checked !== undefined in several places,
causing multiple defects:
- defaultValue: null on IntegerProperty or LongProperty crashed with TypeError (calling null.toString())
- defaultValue: null on DoubleProperty emitted default=0.0
- defaultValue: null on StringProperty, DateTimeProperty, and ObjectProperty emitted default="null"
- lower: null or upper: null on IntegerDomainValidator or LongDomainValidator emitted invalid CTO syntax range=[null, ...]
- lower: null on DoubleDomainValidator emitted range=[0.0, ...]
- minLength: null or maxLength: null on StringLengthValidator emitted length=[null, ...]
- minSize: null or maxSize: null on CollectionSizeValidator emitted size=[null, ...]
- flags: null on StringRegexValidator threw in RegExp constructor, bypassing forward slash escaping
- pattern: null on StringRegexValidator emitted regex=/null/
- all null/omitted bounds on StringLengthValidator emitted length=[,], which causes a syntax error in Parser.parse
- all null/omitted bounds on domain and size validators emitted range=[,] and size=[,], which fail model introspection validation
- ObjectProperty default values containing quotes were interpolated without escaping, breaking parser syntax
- empty decorators arrays emitted spurious newlines and whitespace indentation

Use NullUtil.isNull to safely ignore null bounds and defaults while
preserving falsy non-null values (0, false, ""). Guard validators to only
emit clauses when at least one bound exists. Escape ObjectProperty default
values with toStringLiteral. Safely handle empty decorators and clean regex
flags so pattern normalization succeeds.

Signed-off-by: Siddharth-Dangi <mr.siddharthdangi@gmail.com>
@Siddharth-Dangi
Siddharth-Dangi force-pushed the fix/printer-null-bounds-defaults branch from f0a7dfe to b8e4d7e Compare November 13, 2026 16:51

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant